home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 05 - 1989 / 05.02 Feb 89 / security code / SecurityPatrol.p < prev    next >
Encoding:
Text File  |  1988-11-29  |  11.7 KB  |  490 lines  |  [TEXT/MPS ]

  1. PROGRAM  SecurityPatrol(OUTPUT);
  2. {-------------------------------------------}
  3. (*
  4. ©1988 by Steve Seaquist. All rights reserved.
  5. Used by permission.  Use at your own risk.  
  6. No warranty is expressed or implied.  
  7.  
  8. This Macintosh virus-detecting program was 
  9. originally published and explained in the 
  10. February 1989 issue of MacTutor magazine.  
  11. Some aspects of its design are important to 
  12. security, and it uses some unusual 
  13. techniques, so please read the article.  
  14. *)
  15. {-------------------------------------------}
  16. USES
  17.   MemTypes,QuickDraw,OSIntf,ToolIntf,
  18.   PackIntf,CodeSizeLimits,Globals,
  19.   MainDlog,PasLibIntf,Patrol;
  20. {$R-}
  21.  
  22. CONST
  23.   kPreprocessSelf     = TRUE;
  24.   kAwaitVerification  = FALSE;
  25.  
  26. VAR
  27.   gInitSecPatDone:    BOOLEAN;
  28.   gRptFileOpen:       BOOLEAN;
  29.   gScrDmpEnbPtr:      Ptr;
  30.   gScrDmpEnbSave:     SignedByte;
  31.   o:                  Text;
  32.  
  33. PROCEDURE  PreprocessSelf;           FORWARD;
  34. PROCEDURE  Wryte
  35.   (pStr:     Str255);                FORWARD;
  36. PROCEDURE  WryteChar
  37.   (pChar:    CHAR);                  FORWARD;
  38. PROCEDURE  WryteEoln;                FORWARD;
  39. PROCEDURE  WryteLn
  40.   (pStr:     Str255);                FORWARD;
  41. PROCEDURE  WryteNbr
  42.   (pNbr:     LONGINT;
  43.   pNbrDigits:INTEGER);               FORWARD;
  44. PROCEDURE  WryteType
  45.   (pType:    ResType);               FORWARD;
  46.  
  47. {-------------------------------------------}
  48. {$Z*}
  49. {-------------------------------------------}
  50. PROCEDURE  ExitSecurityPatrol;
  51. BEGIN
  52. IF  gOption[eTrace] THEN
  53.   Trace('ExitSecurityPatrol');
  54. gScrDmpEnbPtr^ := gScrDmpEnbSave;
  55. IF  gRptFileOpen THEN
  56.   Close(o);
  57. ExitToShell;
  58. END;
  59. {-------------------------------------------}
  60. {$Z-}
  61. {-------------------------------------------}
  62. PROCEDURE  InitSecurityPatrol;
  63. VAR
  64.   sConfig:   LONGINT;
  65. BEGIN
  66. MaxApplZone;
  67. MoreMasters; MoreMasters; MoreMasters;
  68. gInitSecPatDone := FALSE;
  69. gRptFileOpen    := FALSE;
  70. gScrDmpEnbPtr   := Ptr(kScrDmpEnb);
  71. gScrDmpEnbSave  := gScrDmpEnbPtr^;
  72. gScrDmpEnbPtr^  := 0;
  73. Textbook(@thePort);
  74. Write  ('SecurityPatrol is a Mac virus ');
  75. Write  ('detector.  ');
  76. TextFace([bold,extend]);
  77. WriteLn('Use at your own risk.  ');
  78. TextFace([]);
  79. Write  ('The Save As... dialog below is ');
  80. WriteLn('to save error reports.');
  81. PLFlush(OUTPUT);
  82. PauseBriefly;
  83.  
  84. InitGlobals;
  85. gOption[eBeeps]   := TRUE; {override…}
  86. gDisabled[eFgPrC] := TRUE; {…defaults}
  87. InitMainDlog;
  88. InitPatrols;
  89.  
  90. sConfig := BAnd(ORD4(Ptr(kSPConfig)^),$F);
  91. IF  (sConfig = useFree)
  92. OR  (sConfig = useAsync) THEN
  93.   SFPutFile
  94.     (gSFPutPt,'Filename, or cancel to print',
  95.     'SecurityPatrol Report',NIL,gSFRep)
  96. ELSE
  97.   SFPutFile
  98.     (gSFPutPt,'Filename, or cancel to quit',
  99.     'SecurityPatrol Report',NIL,gSFRep);
  100. WITH gSFRep DO
  101.   IF good THEN
  102.     BEGIN
  103.     gCurrWDRefNum := vRefNum;
  104.     BuildDirname;
  105.     ReWrite(o,CONCAT(gCurrDirname,fName));
  106.     END
  107.   ELSE
  108.     IF  (sConfig = useFree)
  109.     OR  (sConfig = useAsync) THEN
  110.       ReWrite(o,'PRINTER:')
  111.     ELSE
  112.       BEGIN
  113.       WriteLn('Run cancelled.');
  114.       PLFlush(OUTPUT);
  115.       PauseBriefly;
  116.       ExitSecurityPatrol;
  117.       END;
  118. gRptFileOpen := TRUE;
  119.  
  120. Wryte  ('This copy of Security Patrol 1.0 ');
  121. Wryte  ('is being maintained by ');
  122. TextFace([bold,extend]);
  123. gPgmrname := '<<your name here>>';
  124. WryteLn(gPgmrname);
  125. TextFace([]);
  126. Wryte  ('The following run was done on ');
  127. GetTime(gDateTimeRec);
  128. WITH gDateTimeRec DO
  129.   BEGIN
  130.   year := year mod 100;
  131.   WryteNbr (month,2);
  132.   WryteChar('/');
  133.   WryteNbr (day,  2);
  134.   WryteChar('/');
  135.   WryteNbr (year, 2);
  136.   Wryte    (' at ');
  137.   WryteNbr (hour, 2);
  138.   WryteChar(':');
  139.   IF  minute < 10 THEN
  140.     WryteChar('0');
  141.   WryteNbr (minute,1);
  142.   WryteLn  ('.');
  143.   END;
  144. IF  kPreprocessSelf THEN
  145.   PreprocessSelf
  146. ELSE
  147.   WryteLn('Didn’t perform self-tests.');
  148. gInitSecPatDone := TRUE;
  149. END;
  150. {-------------------------------------------}
  151. PROCEDURE  PreprocessSelf;
  152. VAR
  153.   i:          INTEGER;
  154.   sC1Ptr:     LONGINT;
  155.   sEntActual: LONGINT;
  156.   sEntShouldB:LONGINT;
  157.   sOffActual: LONGINT;
  158.   sOffShouldB:LONGINT;
  159.   sResType:   ResType;
  160.   sSave:      TMainOpt;
  161.   {--------------------}
  162.   PROCEDURE  Abort
  163.     (pStr:     Str255);
  164.   BEGIN
  165.   ErrorBegins(pStr);
  166.   WryteEoln;
  167.   CommentBegins;
  168.   WryteLn('Assuming infected.');
  169.   CommentBegins;
  170.   Wryte  ('Contact ');
  171.   Wryte  (gPgmrname);
  172.   WryteLn(' to be sure.');
  173.   CommentBegins;
  174.   Wryte  ('(These msgs apply to ');
  175.   Wryte  (gCurrFilename);
  176.   WryteLn(' itself, not to your system.)');
  177.   CommentBegins;
  178.   gOption[eAwait] := TRUE;
  179.   gOption[eBeeps] := TRUE;
  180.   ErrorEnds(4);
  181.   ExitSecurityPatrol;
  182.   END;
  183.   {--------------------}
  184. BEGIN
  185. BlockMove(@gOption,@sSave,SIZEOF(TMainOpt));
  186. ZeroOut  (@gOption,       SIZEOF(TMainOpt));
  187. gOption[eRmVir] := TRUE;
  188. { gOption[eTrace] := TRUE; }
  189. IF  gOption[eTrace] THEN
  190.   Trace('PreprocessSelf');
  191. GetCodeSizeLimits;
  192. GetRsrc(@gCode0,'CODE',0,ResId);
  193. IF  (gCode0.fFlag <> kRsrcHdlValid) THEN
  194.   Abort('Unable to get own CODE 0');
  195. IF  NOT(Code0IsValid) THEN
  196.   Abort('Found unexpected CODE 0 header');
  197. LookForKnownViruses;
  198. FOR i := 1 TO Count1Types DO
  199.   BEGIN
  200.   Get1IndType(sResType,i);
  201.   IF  (sResType = 'SIZE') THEN
  202.     BEGIN
  203.     IF  (Count1Resources('SIZE') > 1) THEN
  204.       Abort('Too many SIZE resources');
  205.     GetRsrc(@gCurrRsrc,'SIZE',1,Index);
  206.     IF  (gCurrRsrc.fSize > 10) THEN
  207.       Abort('SIZE resource too large');
  208.     ReleaseRsrc(@gCurrRsrc);
  209.     END
  210.   ELSE IF (sResType <> 'CODE') THEN
  211.     BEGIN
  212.     ErrorBegins('Found a rsrc of type ');
  213.     WryteType(sResType);
  214.     ErrorEnds(0);
  215.     Abort    ('Only CODE and SIZE allowed');
  216.     END;
  217.   END;
  218. WITH TJTHdl(gCode0.fHdl)^^,fJTEntry[1] DO
  219.   BEGIN
  220.   IF  (fNbrBytesInTable <> gJTSize) THEN
  221.     BEGIN
  222.     ErrorBegins('Jump table size is ');
  223.     WryteNbr (fNbrBytesInTable,1);
  224.     Wryte    (', should be ');
  225.     WryteNbr (gJTSize,1);
  226.     ErrorEnds(0);
  227.     Abort    ('Invalid Jump Table size');
  228.     END;
  229.   IF  NOT(JTEIsValid(@fJTEntry[1])) THEN
  230.     Abort('Invalid Jump Table entry');
  231.   IF  (fSegId  <> 1) THEN
  232.     BEGIN
  233.     ErrorBegins('Enters at CODE ');
  234.     WryteNbr(fSegId,1);
  235.     Wryte   (', should enter at CODE 1');
  236.     ErrorEnds(0);
  237.     Abort   ('Invalid start address');
  238.     END;
  239.   sOffActual := 4 + fOffset;
  240.   END;
  241. WITH gCurrRsrc DO
  242.   BEGIN
  243.   GetRsrc(@gCurrRsrc,'CODE',1,ResId);
  244.   IF  (fFlag <> kRsrcHdlValid) THEN
  245.     Abort('Couldn’t look at own CODE 1');
  246.   sC1Ptr      := BAND($00FFFFFF,ORD4(fHdl^));
  247.   sEntActual := sC1Ptr + sOffActual;
  248.   IF  (TWordPtr(sEntActual)^ = $4EFA) THEN
  249.     BEGIN
  250.     sOffActual := 
  251.       sOffActual+2+TWordPtr(sEntActual+2)^;
  252.     sEntActual := sC1Ptr + sOffActual;
  253.     END;
  254.   sEntShouldB := gEntryPoint;
  255.   sOffShouldB := sEntShouldB - sC1Ptr;
  256.   IF  (sEntActual <> sEntShouldB) THEN
  257.     BEGIN
  258.     ErrorBegins('Invalid start address');
  259.     WryteEoln;
  260.     CommentBegins;
  261.     Wryte   ('Enters at address $');
  262.     ShortHexDump(@sEntActual,4);
  263.     Wryte   (' (CODE 1 + $');
  264.     ShortHexDump(@sOffActual,4);
  265.     Wryte   (') --> $');
  266.     ShortHexDump(Ptr(sEntActual),4);
  267.     WryteEoln;
  268.     CommentBegins;
  269.     Wryte   ('Should enter at   $');
  270.     ShortHexDump(@sEntShouldB,4);
  271.     Wryte   (' (CODE 1 + $');
  272.     ShortHexDump(@sOffShouldB,4);
  273.     Wryte   (') --> $');
  274.     ShortHexDump(Ptr(sEntShouldB),4);
  275.     WryteEoln;
  276.     CommentBegins;
  277.     Wryte   ('CODE 1 begins at  $');
  278.     ShortHexDump(@sC1Ptr,4);
  279.     ErrorEnds(0);
  280.     Abort   ('This is not a user error');
  281.     END;
  282.   ReleaseRsrc(@gCurrRsrc);
  283.   END;
  284. ReleaseRsrc(@gCode0);
  285.  
  286. IF  (Count1Resources('CODE')>gMaxCode+1) THEN
  287.   Abort('Too many CODE resources.');
  288. IF  kAwaitVerification THEN
  289.   BEGIN
  290.   ErrorBegins('The following are the ');
  291.   Wryte  ('“fingerprints” of ');
  292.   Wryte  (gCurrFilename);
  293.   Wryte  (' itself:');
  294.   ErrorEnds(0);
  295.   END;
  296. FOR i := 0 TO gMaxCode DO
  297.   WITH gCurrRsrc DO
  298.     BEGIN
  299.     GetRsrc(@gCurrRsrc,'CODE',i,ResId);
  300.     IF  (fFlag <> kRsrcHdlValid) THEN
  301.       Abort('Couldn’t look at next CODE');
  302.     IF  (fSize > gSizeLimit[i]) THEN
  303.       BEGIN
  304.       ErrorBegins('Failed a CODE size test');
  305.       WryteEoln;
  306.       CommentRsrcBegins(@gCurrRsrc);
  307.       Wryte   (' size is ');
  308.       WryteNbr(fSize,1);
  309.       Wryte   (', which exceeds its ');
  310.       WryteNbr(gSizeLimit[i],1);
  311.       WryteLn (' size limit.');
  312.       Abort('May contain an imbedded virus');
  313.       END;
  314.     IF  kAwaitVerification THEN
  315.       BEGIN
  316.       ProcessCurrRsrc;
  317.       CommentFgPrRsrc(@gCurrRsrc);
  318.       END;
  319.     ReleaseRsrc(@gCurrRsrc);
  320.     END;
  321. IF  kAwaitVerification THEN
  322.   BEGIN
  323.   ErrorBegins('Time to make a decision:');
  324.   WryteEoln;
  325.   CommentBegins;
  326.   WryteLn('Verify fingerprints if you can');
  327.   CommentBegins;
  328.   WryteLn('Press command-period to abort');
  329.   CommentBegins;
  330.   WryteLn('Press any other key to continue');
  331.   CommentBegins;
  332.   gOption[eAwait] := TRUE;
  333.   ErrorEnds(0);
  334.   IF  gAbortPatrol THEN
  335.     BEGIN
  336.     PauseBriefly;
  337.     ExitSecurityPatrol;
  338.     END;
  339.   END;
  340. WryteLn('Passed all current self-tests.');
  341. PauseBriefly;
  342. BlockMove(@sSave,@gOption,SIZEOF(TMainOpt));
  343. END;
  344. {-------------------------------------------}
  345. {$Z*}
  346. {-------------------------------------------}
  347. PROCEDURE  WriteFilenameToReport;
  348. BEGIN
  349. WITH gReportFlags DO
  350.   IF  NOT(fWroteFilename) THEN
  351.     BEGIN
  352.     IF  NOT(fWroteDirname) THEN
  353.       BEGIN
  354.       WriteLn(o,gCurrDirname);
  355.       fWroteDirname := TRUE;
  356.       END;
  357.     Write(o,gInd,gCurrFilename);
  358.     IF  gActiveSelf THEN
  359.       BEGIN
  360.       Write(o,' (Active Self)');
  361.       IF  gInitSecPatDone
  362.       AND NOT(kProcessSelf) THEN
  363.         Write(o,' skipped');
  364.       END
  365.     ELSE IF gActiveSys THEN
  366.       Write(o,' (Active System)');
  367.     WriteLn(o);
  368.     fWroteFilename := TRUE;
  369.     END;
  370. END;
  371. {-------------------------------------------}
  372. PROCEDURE  WriteFilenameToScreen;
  373. BEGIN
  374. WITH gScreenFlags DO
  375.   IF  NOT(fWroteFilename) THEN
  376.     BEGIN
  377.     IF  NOT(fWroteDirname) THEN
  378.       BEGIN
  379.       WriteLn(gCurrDirname);
  380.       fWroteDirname := TRUE;
  381.       END;
  382.     Write(gInd,gCurrFilename);
  383.     IF  gActiveSelf THEN
  384.       BEGIN
  385.       Write(' (Active Self)');
  386.       IF  gInitSecPatDone
  387.       AND NOT(kProcessSelf) THEN
  388.         Write(' skipped');
  389.       END
  390.     ELSE IF gActiveSys THEN
  391.       Write(' (Active System)');
  392.     WriteLn;
  393.     PLFlush(OUTPUT);
  394.     fWroteFilename := TRUE;
  395.     END;
  396. END;
  397. {-------------------------------------------}
  398. PROCEDURE  Wryte
  399.   (pStr:     Str255);
  400. BEGIN
  401. Write(pStr);
  402. IF  gRptFileOpen THEN
  403.   Write(o,pStr);
  404. END;
  405. {-------------------------------------------}
  406. PROCEDURE  WryteChar
  407.   (pChar:    CHAR);
  408. BEGIN
  409. Write(pChar);
  410. IF  gRptFileOpen THEN
  411.   Write(o,pChar);
  412. END;
  413. {-------------------------------------------}
  414. PROCEDURE  WryteEoln;
  415. BEGIN
  416. WriteLn;
  417. PLFlush(OUTPUT);
  418. IF  gRptFileOpen THEN
  419.   BEGIN
  420.   WriteLn(o);
  421.   { PLFlush(o); control w/option chk box? }
  422.   END;
  423. END;
  424. {-------------------------------------------}
  425. PROCEDURE  WryteFilename;
  426. BEGIN
  427. IF  gRptFileOpen THEN
  428.   WriteFilenameToReport;
  429. WriteFilenameToScreen;
  430. END;
  431. {-------------------------------------------}
  432. PROCEDURE  WryteFilenameToScreenOnlyForNow;
  433. BEGIN
  434. WriteFilenameToScreen;
  435. END;
  436. {-------------------------------------------}
  437. PROCEDURE  WryteLn
  438.   (pStr:     Str255);
  439. BEGIN
  440. WriteLn(pStr);
  441. PLFlush(OUTPUT);
  442. IF  gRptFileOpen THEN
  443.   BEGIN
  444.   WriteLn(o,pStr);
  445.   { PLFlush(o); control w/option chk box? }
  446.   END;
  447. END;
  448. {-------------------------------------------}
  449. PROCEDURE  WryteNbr
  450.   (pNbr:     LONGINT;
  451.   pNbrDigits:INTEGER);
  452. BEGIN
  453. Write(pNbr:pNbrDigits);
  454. IF  gRptFileOpen THEN
  455.   Write(o,pNbr:pNbrDigits);
  456. END;
  457. {-------------------------------------------}
  458. PROCEDURE  WryteType
  459.   (pType:    ResType);
  460. BEGIN
  461. Write(pType);
  462. IF  gRptFileOpen THEN
  463.   Write(o,pType);
  464. END;
  465. {-------------------------------------------}
  466. PROCEDURE  zzSecurityPatrol;
  467. BEGIN
  468. END;
  469. {*******************************************}
  470. BEGIN
  471. InitSecurityPatrol;
  472. WHILE TRUE DO
  473.   BEGIN
  474.   gAbortPatrol := FALSE;
  475.   CASE MainDlogWorkRequested OF
  476.   eDirs:    PatrolDirectories (FALSE);
  477.   eDiry:    PatrolDirectories (TRUE);
  478.   eEvery:   PatrolEverything;
  479.   eFiles:   PatrolFiles;
  480.   OTHERWISE LEAVE;
  481.   END; {CASE}
  482.   END;
  483. WryteEoln;
  484. WryteLn('*******************************');
  485. WryteEoln;
  486. WryteLn ('Totals over all patrols:');
  487. ListCounts(@gTotals);
  488. ExitSecurityPatrol;
  489. {*******************************************}
  490. END.